package com.njbw.design_pattern.struct.proxy.dynamicProxy.cglib;


import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;

/**
 *
 * @author 黄侯淳
 * @since JDK 1.8
 */
@Slf4j
public class CacheMethodInterceptor implements MethodInterceptor {

    // 可以自行改造成为具有 LRU 淘汰功能的缓存
    private final HashMap<String, String> cache = new LinkedHashMap<>(256);

    /**
     * 拦截方法的调用
     *
     * @param obj    表示要进行增强的对象（代理对象，被代理类动态生成子类的实例）
     * @param method 表示拦截的方法
     * @param args   数组表示参数列表，基本数据类型需要传入其包装类型，如int-->Integer、long-Long、double-->Double
     * @param proxy  表示对方法的代理，invokeSuper方法表示对被代理对象方法的调用
     * @return 执行结果
     * @throws Throwable 异常
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // log.info("obj: {}", obj);
        log.info("method: {}, args: {}", method.getName(), Arrays.toString(args));
        // log.info("proxy: {}", proxy);
        // 1.判断是哪一个方法，只对 query 方法进行缓存
        String result;
        if ("query".equals(method.getName())) {
            // 2.先从缓存中查询，命中直接返回
            result = cache.get(args[0].toString());
            if (null != result) {
                log.info("缓存命中，直接返回缓存结果...");
                return result;
            }

            // 3.缓存未命中，查询数据库（需要被代理实例）
            // 注意这里是调用 invokeSuper 而不是 invoke，否则死循环;
            // methodProxy.invokeSuper 执行的是原始类（被代理类）的方法;
            // method.invoke 执行的是子类（代理类）的方法;
            result = (String) proxy.invokeSuper(obj, args);

            // 4.如果查询到了，需要进行缓存
            if (null != result) {
                cache.put(args[0].toString(), result);
            }
            return result;
        }

        // 当其它的方法被调用，不希望被干预/增强，直接调用被代理实例的原生方法
        return proxy.invokeSuper(obj, args);
    }
}
